package hotnet.service;

import hotnet.common.IoServiceStatistics;
import hotnet.common.NamedRunnable;
import hotnet.config.DefaultIoSocketSessionConfig;
import hotnet.config.IoSessionConfig;
import hotnet.exception.ExceptionMonitor;
import hotnet.filter.DefaultIoFilterChain;
import hotnet.filter.DefaultIoFilterChainBuilder;
import hotnet.filter.IoFilterChain;
import hotnet.filter.IoFilterChainBuilder;
import hotnet.future.ConnectFuture;
import hotnet.future.IoFuture;
import hotnet.handler.IoHandler;
import hotnet.listener.IoServiceListener;
import hotnet.listener.IoServiceListenerManager;
import hotnet.session.DefaultIoSessionAttributeMap;
import hotnet.session.IoSession;
import hotnet.session.IoSessionManager;

import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public abstract class AbstractIoService implements IoService {
	private static final AtomicInteger id = new AtomicInteger();
	private final IoServiceListenerManager listenerManager;
	private final IoSessionManager sessionManager;
	private final IoServiceStatistics statistics;
	private final IoSessionConfig defaultSessionConfig;
	private IoHandler handler;
	
	private final IoFilterChainBuilder filterChainBuilder;
	
    protected final Object disposalLock = new Object();
    private volatile boolean disposing;
    private volatile boolean disposed;
    
    private Executor executor;
    
    private String serviceName;
	
	public AbstractIoService(IoSessionConfig config) {
		listenerManager = new IoServiceListenerManager(this);
		sessionManager = new IoSessionManager(this);
		statistics = new IoServiceStatistics();
		
		defaultSessionConfig = config;
		executor = Executors.newCachedThreadPool();
		
		serviceName = getClass().getSimpleName() + id.getAndIncrement();

		filterChainBuilder = new DefaultIoFilterChainBuilder();
	}

	public IoFilterChainBuilder getIoFilterChainBuilder() {
		return filterChainBuilder;
	}
	
	@Override
	public IoSessionConfig getDefaultSessionConfig() {
		return defaultSessionConfig;
	}
	
	public void addListener(IoServiceListener listener) {
		this.listenerManager.addListener(listener);
	}
	
	public void removeListener(IoServiceListener listener) {
		this.listenerManager.remove(listener);
	}
	
	public IoServiceListenerManager getListenerManager() {
		return this.listenerManager;
	}
	
	/* service at less has one session */
	public boolean isActive() {
		return this.listenerManager.isActive();
	}
	
	public boolean isDisposing() {
		return disposing;
	}
	
	public boolean isDisposed() {
		return disposed;
	}
	
	public void dispose() {
		dispose(false);
	}
	
	public void dispose(boolean awaitTermination) {
		if (disposed) {
			return;
		}
		
		synchronized (disposalLock) {
			if (disposed)
				return;
			
			if (!disposing) {
                disposing = true;

                try {
                    dispose0();
                } catch (Exception e) {
                    ExceptionMonitor.getInstance().exceptionCaught(e);
                }
                
                
                ExecutorService e = (ExecutorService) executor;
                e.shutdownNow();
                
                try {
					e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
				} catch (InterruptedException e1) {
	                 Thread.currentThread().interrupt();
				}
            }
        }
		
		disposed = true;
	}
	
	protected abstract void dispose0() throws Exception;
	
	public IoHandler getHandler() {
		return this.handler;
	}
	public void setHandler(IoHandler handler) {
		this.handler = handler;
	}
	
	public Map<Long, IoSession> getManagedSessions() {
		return sessionManager.getManagedSessions();
	}
	
	public IoServiceStatistics getStatistics() {
		return statistics;
	}
	
	public void startRunService(Runnable runnable) {
		executor.execute(new NamedRunnable(runnable, serviceName));
	}
	
	public void initSession(IoSession session, IoFuture future) {
		if (future instanceof ConnectFuture) {
			session.setAttribute(DefaultIoSessionAttributeMap.CONNECT_FUTURE, future);
		}
	}
	
	@Override
	public IoService getService() {
		return this;
		
	}
}
